package exports

import (
	"fmt"
	"reflect"
	"strings"
)

// ExportConvert 导出转换
type ExportConvert interface {
	Convert(records interface{}) []string
	TagName() string
}

// CsvExport csv导出处理结构体
type CsvExport struct{}

// Convert 转为csv格式,records只能是 []struct{...} 或 []*struct{...} 格式
func (exp *CsvExport) Convert(records interface{}) (result []string) {
	// 仅支持切片
	t := reflect.TypeOf(records)

	if t.Kind() != reflect.Slice {
		return nil
	}
	et := t.Elem()
	for et.Kind() == reflect.Ptr {
		et = et.Elem()
	}
	if et.Kind() != reflect.Struct {
		return nil
	}
	//解析 header tags
	headerTags := exp.parseTags(et)
	// 没有标签直接返回
	if len(headerTags) == 0 {
		return nil
	}

	//解析 field values
	tv := reflect.ValueOf(records)
	result = append(result, strings.Join(headerTags, ","))
	for i := 0; i < tv.Len(); i++ {
		values := exp.parseValues(et, tv.Index(i))
		result = append(result, strings.Join(values, ","))
	}
	return result
}

func (exp *CsvExport) TagName() string {
	return "xlsx"
}

// parseTags 解析tag值
func (exp *CsvExport) parseTags(et reflect.Type) []string {
	names := []string{}

	for i := 0; i < et.NumField(); i++ {
		f := et.Field(i)

		if f.Type.Kind() == reflect.Ptr {
			names = append(names, exp.parseTags(f.Type.Elem())...)
		}
		if f.Type.Kind() == reflect.Struct {
			names = append(names, exp.parseTags(f.Type)...)
		}

		if tags, exist := f.Tag.Lookup(exp.TagName()); exist {
			if name, _ := exp.parseStructTags(tags); "-" != name {
				names = append(names, name)
			}
		}
	}
	return names
}

// parseValues 解析values
func (exp *CsvExport) parseValues(et reflect.Type, ev reflect.Value) []string {
	values := []string{}
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("recover: %s\n", err)
			fmt.Printf("params: %+v,%+v\n", et, ev)
		}
	}()
	if ev.Kind() == reflect.Ptr {
		ev = ev.Elem()
	}
	if ev.IsZero() {
		return []string{}
	}

	for i := 0; i < et.NumField(); i++ {
		f := et.Field(i)
		fv := ev.Field(i)

		if f.Type.Kind() == reflect.Ptr {
			values = append(values, exp.parseValues(f.Type.Elem(), fv.Elem())...)
		}
		if f.Type.Kind() == reflect.Struct {
			values = append(values, exp.parseValues(f.Type, fv)...)
		}

		if tags, exist := f.Tag.Lookup(exp.TagName()); exist {
			if name, _ := exp.parseStructTags(tags); "-" != name {
				values = append(values, fmt.Sprint(fv.Interface()))
			}
		}
	}
	return values
}

func (exp *CsvExport) parseStructTags(tags string) (name string, opts map[string]string) {
	opts = make(map[string]string)
	for _, value := range strings.Split(tags, ";") {
		v := strings.Split(value, ":")
		k := strings.TrimSpace(v[0])
		if len(v) >= 2 {
			opts[k] = strings.Join(v[1:], ":")
		} else {
			name = k
		}
	}
	return name, opts
}
